/**
*
QuickUML; A simple UML tool that demonstrates one use of the
Java Diagram Package
Copyright (C) 2001 Eric Crahen <crahen@cse.buffalo.edu>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package uml.builder;
import java.util.Iterator;
import java.util.Vector;
import util.WrappedIterator;
/**
* @class MetaClass
*
* @date 08-20-2001
* @author Eric Crahen
* @version 1.0
*
* This is a MetaObject for describing Classes. In also includes some simplistic validation
* where possible for things like multiple inheritance, cyclical inheritance and names
*/
public class MetaClass extends MetaComponent {
private NamingComponent attributeNamingComponent = new AttributeNamingComponent();
private MetaClass superClass;
private boolean anInterface;
private Vector interfaceList = new Vector();
private Vector attributeList;
private Vector methodList;
/**
* Create a new MetaClass
*
* @param String name
* @param boolean interface flag
*/
public MetaClass(String name, boolean anInterface) {
isInterface(anInterface);
setName(name);
}
/**
* Set the name of this MetaClass. Performs a simple validaton on the class
* name to make sure that it is vaguely valid (not null & starts with a letter).
*
* @exception SyntaxException - throw if the class name is not valid
*/
public void setName(String name) {
// Check for empty names
int len = (name == null) ? 0 : name.length();
if(len < 1)
throw new SyntaxException(getType() + " has no name!");
// Simple check for invalid class names
if(!Character.isLetter(name.charAt(0)))
throw new SyntaxException(getType() + " must start with a letter!");
super.setName(name);
}
/**
* Get the name of this MetaClass, decorated mainly for user messages
*
* @return String proper name for the class
*/
public String getProperName() {
return getType() + " '" + getName() + "'";
}
/**
* Test weather or not this MetaClass represents an interface.
*
* @return boolean
*/
public boolean isInterface() {
return anInterface;
}
/**
* Set weather or not this MetaClass represents an interface.
*
* @param boolean
*/
public void isInterface(boolean anInterface) {
this.anInterface = anInterface;
}
/**
* Get the type of MetaClass
*
* @return String "interface" or "class"
*/
public String getType() {
return isInterface() ? "interface" : "class";
}
/**
* Set the superclass for this MetaClass
*
* @param MetaClass
*/
public void setSuperClass(MetaClass c) {
setSuperClass(c, false);
}
/**
* Set the superclass for this MetaClass, check for possible multiple inhertence
* or cyclical inheritance.
*
* @param MetaClass new super class
* @param boolean should validate
*
* @exception SyntaxException thrown when inheritance between invalid types is
* attempted
*
* @exception SemanticException thrown when multiple or cyclical inheritance is
* discovered when validating
*/
public void setSuperClass(MetaClass c, boolean validate) {
if(c == null || !getType().equals(c.getType()))
throw new SyntaxException("invalid super class");
if(validate) {
if(superClass != null)
throw new SemanticException("multiple inheritence detected in " + getProperName());
// Find all the classes immediately involved in any multiple inheritance
else if(c.isAncestorOf(this)) {
String msg = "cyclical " + getType() + " inheritance detected in " + "'" + getName();
for(MetaClass meta = c; meta != this; meta= meta.getSuperClass())
msg += ", " + meta.getName();
msg += "'";
throw new SemanticException(msg);
}
}
superClass = c;
}
/**
* Get the MetaClass representing the super class
*
* @return MetaClass
*/
public MetaClass getSuperClass() {
return superClass;
}
/**
* Test if this MetaClass is a child of another MetaClass
*
* @param MetaClass class to test
* @return boolean
*/
public boolean isAncestorOf(MetaClass c) {
for(MetaClass meta = getSuperClass(); meta != null; meta = meta.getSuperClass())
if(meta.equals(c))
return true;
return false;
}
/**
* Add an interface to this MetaClass
*
* @param MetaClass representing the interface to add
*/
public void addInterface(MetaClass c) {
addInterface(c, true);
}
/**
* Add an interface to this MetaClass
*
* @param MetaClass representing the interface to add
* @param boolean should validate
*
* @exception SyntaxException thrown when realization between invalid types is
* attempted
*
* @exception SemanticException thrown when reduntant interfaces are detected
* @post the MetaClass is always left with smallest set of interfaces that define
* the desired contract, redunacies are removed, even when validated
*/
public void addInterface(MetaClass c, boolean validate) {
String msg = null;
if(!c.isInterface())
throw new SyntaxException("invalid interface " + c.getProperName() + " on " + getProperName());
// Redundant interface check, phase 1 - immediate realizations
// of the interface class or a subclass of the interface class
if(hasInterface(c)) {
if(validate)
throw new SemanticException("redundant interface " + c.getProperName() + " on " + getProperName());
// Redundant interface check, phase 2 - indirect realizations
// of the interface through a super class of the interface class
for(Iterator i = interfaceList.iterator(); i.hasNext(); ) {
// If such a match is found on this class, the super interface should be removed
// and the sub interface put in its place
MetaClass interfaceClass = (MetaClass)i.next();
if(c.isAncestorOf(interfaceClass)) {
i.remove();
msg = "redundant interface " + interfaceClass.getProperName() + " on " + getProperName();
break;
}
}
}
interfaceList.add(c);
if(msg != null && validate)
throw new SemanticException(msg);
}
/**
* Remove an interface from this MetaClass
*
* @param MetaClass representing the interface to remove
*/
public void removeInterface(MetaClass c) {
interfaceList.remove(c);
}
/**
* Search the interfaces implemented by this MetaClass for an interface that is
* compatible with he specified interface
*
* @param MetaClass
* @return MetaClass - either the MetaClass specified or a subclass that will work
*/
public MetaClass getCompatibleInterface(MetaClass metaClass) {
if(!metaClass.isInterface())
throw new IllegalArgumentException();
// Check ancestory of interfaces for this class
for(int i = 0; i < interfaceList.size(); i++) {
MetaClass interfaceClass = (MetaClass)interfaceList.elementAt(i);
if(interfaceClass.equals(metaClass) || interfaceClass.isAncestorOf(metaClass))
return interfaceClass;
}
return null;
}
/**
* Test if the MetaClass implements an interface, or a super class.
*
* @param MetaClass interface to test for
* @param boolean check super classes
*
* @return boolean
*/
public boolean hasInterface(MetaClass metaClass) {
return hasInterface(metaClass, true);
}
public boolean hasInterface(MetaClass metaClass, boolean checkParents) {
if(getCompatibleInterface(metaClass) != null)
return true;
if(!checkParents)
return false;
// Check ancestory of interfaces for super classes
MetaClass superClass = getSuperClass();
return (superClass == null) ? false : superClass.hasInterface(metaClass, true);
}
/**
* Get an Iterator over the interfaces for this MetaClass
*
* @return Iterator
*/
public Iterator getInterfaces() {
return getInterfaces(true);
}
protected Iterator getInterfaces(boolean readOnly) {
return new WrappedIterator(interfaceList.iterator(), readOnly);
}
/**
* Get a component that can be used to create names for the collection
* of attributes.
*
* @return NamingComponent
*/
protected NamingComponent getNamingComponent() {
return attributeNamingComponent;
}
/**
* Add a MetaAttribute to this class
*
* @param MetaAttribute
*/
public void addAttribute(MetaAttribute attr) {
if(isInterface())
throw new SemanticException("interface may not have attributes " + getProperName());
getNamingComponent().nameComponent(attr);
if(attributeList == null)
attributeList= new Vector();
attributeList.add(attr);
}
public Iterator getAttributes() {
return getAttributes(true);
}
protected Iterator getAttributes(boolean readOnly) {
return new WrappedIterator(attributeList == null ? null : attributeList.iterator(), readOnly);
}
/**
* Add a MetaMethod to this class
*
* @param MetaAttribute
* @param boolean replace existing method
*/
public void addMethod(MetaMethod m) {
addMethod(m, false);
}
/**
* Add a MetaMethod to this class
*
* @param MetaAttribute
* @param boolean replace existing method
*/
public void addMethod(MetaMethod m, boolean replace) {
String name = m.getName();
if(getName().equals(name))
throw new SemanticException("invalid method name for this class '" + m + "'");
if(name.equals("<init>") && isInterface())
throw new SemanticException("interface can not have constructor this class '" + m + "'");
if(methodList != null) {
if(methodList.contains(m)) {
if(!replace)
throw new SemanticException("duplicate method signature");
else methodList.remove(m);
}
}
if(methodList == null)
methodList = new Vector();
// Insert constructors at the front of the list
if(name.equals("<init>"))
methodList.add(0, m);
else
methodList.add(m);
}
public Iterator getMethods() {
return getMethods(true);
}
protected Iterator getMethods(boolean readOnly) {
return new WrappedIterator(methodList == null ? null : methodList.iterator(), readOnly);
}
/**
* Comparison is performed by comparing the class name for this MetaClass
*
* @param Object
* @return int
*/
public int compareTo(Object o) {
String name = getName();
if(o instanceof MetaClass)
return name.compareTo(((MetaClass)o).getName());
return name.compareTo((String) o);
}
/**
* Human readable string represnting the class, its inheritance and its interfaces.
*
* @return String
*/
public String toString() {
StringBuffer buf = new StringBuffer(getType());
buf.append(' ').append(getName());
if(getSuperClass() != null)
buf.append(" extends ").append(getSuperClass().getName());
for(int i = 0; i < interfaceList.size(); i++) {
if(i == 0)
buf.append(" implements ");
buf.append(((MetaClass)interfaceList.elementAt(i)).getName());
if(i < interfaceList.size() - 1)
buf.append(", ");
}
return buf.toString();
}
}